#pragma once
#include <iostream>
#include <unordered_map>
#include "epoller.hpp"
#include "connection.hpp"

/*
    事件派发模块：反应堆模式的事件派发器
    基于epoll模块监听文件描述符，在epoll模块基础上，用容器帮我们维护一个个connection连接对象
    每个connection连接对象是一个用户要监听的文件描述符、监听的事件、事件就绪的后的IO处理方法的集合
    当epoll监听到事件就绪之后，reactor会自动进行事件的派发以及执行IO回调
*/
class Reactor
{
private:
    const static int default_num = 10;
    const static int default_fd = -1;
    const static int default_timeout = 3000; // 单位ms

    // 一次派发
    void loopOnce(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = _ev_arry[i].data.fd;
            uint32_t evts = _ev_arry[i].events;

            // 下面将出现的所有错误归纳到读写错误中去处理
            // 自己方socket出现问题才会触发epollerr epollhup
            // 对方主动关闭socket：epollrdhup epollin epollout
            if (evts & EPOLLRDHUP)
            {
                // std::cout << "EPOLLRDHUP" << endl;
                evts |= EPOLLIN | EPOLLOUT;
            }

            if (evts & EPOLLERR)
            {
                // std::cout << "EPOLLRDHUP" << endl;
                evts |= EPOLLIN | EPOLLOUT;
            }

            if (evts & EPOLLIN)
            {
                // std::cout << "触发读事件\n";
                if (_connections.find(fd) != _connections.end() && _connections[fd]->_recver != nullptr)
                {
                    _connections[fd]->_recver(_connections[fd]);
                }
            }

            if (evts & EPOLLOUT)
            {
                // std::cout << "触发写事件\n";
                if (_connections.find(fd) != _connections.end() && _connections[fd]->_recver != nullptr)
                {
                    _connections[fd]->_sender(_connections[fd]);
                }
            }
        }
    }

    // debug查看所有检测的fd
    void debugPrintFd(int n)
    {
        LOG(DEBUG, "all fd:");
        for (auto &e : _connections)
            std::cout << e.first << " ";
        std::cout << endl;
    }

public:
    Reactor() : _isrunning(false), _ev_arry(new epoll_event[default_num])
    {
    }

    // 添加监听文件描述符、注册事件、以及事件就绪的IO回调方法到reactor
    void addConnection(const int fd, const uint32_t events, func_t recver, func_t sender, func_t excepter)
    {
        // 1、创建Conncetion对象
        Connection *cnt = new Connection(fd, events, recver, sender, excepter, this);

        // 2、将Connector对象添加到容器中维护
        _connections.insert({fd, cnt});

        // 3、将监听文件描述符添加到epoll模型中
        _epoller.addEvent(fd, events);
    }

    // 连接connection关系的读写事件使能
    void enableWriteRead(int fd, bool readable, bool writeable)
    {
        uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        if (_connections.find(fd) != _connections.end())
        {
            // 1、修改_connections容器中fd对应连接的关心事件
            _connections[fd]->setEvent(event);

            // 2、修改epoll模型中fd的关心事件
            _epoller.modEvent(fd, event);
        }
    }

    // 删除连接
    void delConnection(const int &fd)
    {
        // 1、从_connections容器中删除维护的连接，并释放空间
        std::unordered_map<int, Connection *>::iterator it = _connections.find(fd);
        if (it == _connections.end())
            return;
        delete it->second;      // 释放动态开辟的空间
        _connections.erase(fd); // 从容器中删除

        // 2、从epoll模型中删除文件描述符
        _epoller.delEvent(fd);

        // 3、关闭文件描述符释放内核数据结构
        close(fd);
    }

    // 事件派发
    void dispatch()
    {
        _isrunning = true;
        while (_isrunning)
        {
            int n = _epoller.wait(_ev_arry, default_num, default_timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "epoll timeout!\n");
                break;
            case -1:
                LOG(ERROR, "epoll error!\n");
                _isrunning = false;
                break;
            default:
                LOG(INFO, "events is ready!\n");
                loopOnce(n);
                debugPrintFd(n); // 调试
                break;
            }
        }
    }

    ~Reactor()
    {
        delete[] _ev_arry;
    }

private:
    std::unordered_map<int, Connection *> _connections; // 维护要监听的链接对象的容器
    Epoller _epoller;                                   // epoll模型
    struct epoll_event *_ev_arry;                       // 接收就绪事件的数组缓冲区
    bool _isrunning;
};